using System;
using System.Collections.Generic;
using System.IO;
using System.Text;

using ICSharpCode.SharpZipLib.Zip;

namespace GenesisCore
{
	class RomLoader
	{
		public static void Load(string fileName, byte[] memory)
		{
			using (FileStream fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read))
			{
				if (fileName.ToUpper().EndsWith(".BIN"))
				{
					LoadBIN(fileStream, memory, (int)fileStream.Length);
				}
				else if (fileName.ToUpper().EndsWith(".SMD"))
				{
					LoadSMD(fileStream, memory);
				}
				else if (fileName.ToUpper().EndsWith(".ZIP"))
				{
					LoadZIP(fileStream, memory);
				}
				else
				{
					throw new ArgumentException("Unrecognized file type");
				}
			}
		}

		private static void LoadBIN(Stream binStream, byte[] memory, int length)
		{
			binStream.Read(memory, 0, length);
		}

		private static void LoadSMD(Stream smdStream, byte[] memory)
		{
			byte[] block = new byte[0x4000];

			using (MemoryStream memoryStream = new MemoryStream(memory))
			{
				// Skip over SMD header
				smdStream.Read(block, 0, 0x200);

				int bytesRead = smdStream.Read(block, 0, block.Length);
				while (bytesRead == 0x4000)
				{
					byte[] decodedBlock = Decode(block);
					memoryStream.Write(decodedBlock, 0, decodedBlock.Length);

					bytesRead = smdStream.Read(block, 0, block.Length);
				}
			}
		}

		private static byte[] Decode(byte[] block)
		{
			byte[] decodedBlock = new byte[block.Length];
			int midPoint = block.Length / 2;

			for (int i = 0; i < midPoint; i++)
			{
				decodedBlock[i * 2 + 1] = block[i];
			}

			for (int i = midPoint; i < block.Length; i++)
			{
				decodedBlock[(i - midPoint) * 2] = block[i];
			}

			return decodedBlock;
		}

		private static void LoadZIP(Stream zipStream, byte[] memory)
		{
			ICSharpCode.SharpZipLib.Zip.ZipFile zipFile = new ZipFile(zipStream);

			for (int i = 0; i < zipFile.Count; i++)
			{
				ZipEntry entry = zipFile[i];
				if (entry.Name.ToUpper().EndsWith(".SMD"))
				{
					using (Stream smdStream = zipFile.GetInputStream(entry))
					{
						LoadSMD(smdStream, memory);
					}
				}
				else if (entry.Name.ToUpper().EndsWith(".BIN"))
				{
					using (Stream binStream = zipFile.GetInputStream(entry))
					{
						LoadBIN(binStream, memory, (int)entry.Size);
					}
				}
			}
		}
	}
}
